<script setup lang="ts">
import {
  Cartesian3,
  Math as CesiumMath,
  Terrain,
  Viewer,
  Transforms,
  HeadingPitchRoll,
  HeadingPitchRange,
  Model,
  Ellipsoid,
  ModelAnimationLoop,
  Matrix4,
  JulianDate,
  SampledPositionProperty,
  PolylineGlowMaterialProperty,
  Color,
  HeightReference,
} from "cesium";
import { onMounted } from "vue";

onMounted(() => {
  init();
});

const init = async () => {
  const viewer = new Viewer("cesiumContainer", {
    requestRenderMode: true,
    shouldAnimate: true,
    terrain: Terrain.fromWorldTerrain(),
    infoBox: false,
    animation: false,
    timeline: false,
    navigationHelpButton: false,
    sceneModePicker: false,
    baseLayerPicker: true,
    fullscreenButton: false,
    geocoder: false,
    homeButton: false,
    scene3DOnly: true,
  });

  (viewer.cesiumWidget.creditContainer as HTMLElement).style.display = "none";

  const canvas = viewer.canvas;
  canvas.setAttribute("tabindex", "0"); // needed to put focus on the canvas
  canvas.addEventListener("click", function () {
    canvas.focus();
  });
  canvas.focus();

  const scene = viewer.scene;
  const camera = scene.camera;

  const pathPosition = new SampledPositionProperty();
  viewer.entities.add({
    position: pathPosition,
    name: "path",
    path: {
      show: true,
      leadTime: 0,
      trailTime: 60,
      width: 10,
      resolution: 1,
      material: new PolylineGlowMaterialProperty({
        glowPower: 0.3,
        taperPower: 0.3,
        color: Color.PALEGOLDENROD,
      }),
    },
  });

  const controller = scene.screenSpaceCameraController;
  let r = 0;

  const hpRoll = new HeadingPitchRoll();
  const hpRange = new HeadingPitchRange();
  let speed = 1;
  const deltaRadians = CesiumMath.toRadians(3.0);

  let position = Cartesian3.fromDegrees(-123.0744619, 44.0503706, 120.0);
  let speedVector = new Cartesian3();

  const fixedFrameTransform = Transforms.localFrameToFixedFrameGenerator(
    "north",
    "west"
  );

  try {
    const planePrimitive = scene.primitives.add(
      await Model.fromGltfAsync({
        // url: "/static/models/CesiumAir.glb",
        url: "/static/models/character-male-e.glb",
        modelMatrix: Transforms.headingPitchRollToFixedFrame(
          position,
          hpRoll,
          Ellipsoid.WGS84,
          fixedFrameTransform
        ),
        minimumPixelSize: 16 * 8,
        debugShowBoundingVolume: false,
        heightReference: HeightReference.CLAMP_TO_GROUND,
        scene: scene,
      })
    );

    let isModelLoaded = false;
    planePrimitive.readyEvent.addEventListener(() => {
      isModelLoaded = true;

      // Zoom to model
      const max = Math.max(
        planePrimitive.boundingSphere.radius,
        camera.frustum.near
      );
      r = 2.0 * max;
      controller.minimumZoomDistance = r * 0.5;
      const center = planePrimitive.boundingSphere.center;
      const heading = CesiumMath.toRadians(180.0);
      const pitch = CesiumMath.toRadians(-20.0);
      hpRange.heading = heading;
      hpRange.pitch = pitch;
      hpRange.range = r * 40.0;
      camera.lookAt(center, hpRange);

      // heading=2.9667965665224907
      // pitch=-0.3332142436320136
      // roll=1.1273387556798298e-8
    });

    let isForward = false;

    document.addEventListener("keydown", function (e) {
      // console.log("keydown", e.code);

      if (!isModelLoaded) return;

      switch (e.code) {
        case "ArrowDown":
          if (e.shiftKey) {
            // speed down
            speed = Math.max(--speed, 1);
          } else {
            // pitch down
            hpRoll.pitch -= deltaRadians;
            if (hpRoll.pitch < -CesiumMath.TWO_PI) {
              hpRoll.pitch += CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowUp":
          if (e.shiftKey) {
            // speed up
            speed = Math.min(++speed, 100);
          } else {
            // pitch up
            hpRoll.pitch += deltaRadians;
            if (hpRoll.pitch > CesiumMath.TWO_PI) {
              hpRoll.pitch -= CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowRight":
          if (e.shiftKey) {
            // roll right
            hpRoll.roll += deltaRadians;
            if (hpRoll.roll > CesiumMath.TWO_PI) {
              hpRoll.roll -= CesiumMath.TWO_PI;
            }
          } else {
            // turn right
            hpRoll.heading += deltaRadians;
            if (hpRoll.heading > CesiumMath.TWO_PI) {
              hpRoll.heading -= CesiumMath.TWO_PI;
            }
          }
          break;
        case "ArrowLeft":
          if (e.shiftKey) {
            // roll left until
            hpRoll.roll -= deltaRadians;
            if (hpRoll.roll < 0.0) {
              hpRoll.roll += CesiumMath.TWO_PI;
            }
          } else {
            // turn left
            hpRoll.heading -= deltaRadians;
            if (hpRoll.heading < 0.0) {
              hpRoll.heading += CesiumMath.TWO_PI;
            }
          }
          break;
        case "KeyW":
          fakeW();
          break;
        case "Space":
          autoAun();
          break;
        default:
      }

      if (!isForward) {
        const { center } = planePrimitive.boundingSphere;

        // 转动模型
        // const { modelMatrix } = planePrimitive;
        // Transforms.headingPitchRollToFixedFrame(
        //   center,
        //   hpRoll,
        //   Ellipsoid.WGS84,
        //   fixedFrameTransform,
        //   modelMatrix
        // );

        hpRange.heading = hpRoll.heading; // 镜头前后
        hpRange.pitch = hpRoll.pitch - 0.05 * Math.PI; // 镜头上下
        hpRange.range = r * 10; // 镜头远近
        camera.lookAt(center, hpRange);
      }

      scene.requestRender();
    });

    document.addEventListener("keyup", function (e) {
      // console.log("keyup", e.code);

      if (!isModelLoaded) return;

      if (["KeyW"].includes(e.code)) {
        isForward = false;

        autoAun();
      }
    });

    const fakeW = () => {
      if (!isForward) {
        isForward = true;

        autoAun();
      }
    };

    let flag: number | null = null;
    const autoAun = () => {
      if (flag) {
        // console.log("动画移除了");

        planePrimitive.activeAnimations.removeAll();

        cancelAnimationFrame(flag);
        flag = null;
      } else {
        // console.log("动画添加了");

        planePrimitive.activeAnimations.add({
          index: 2,
          multiplier: 0.5,
          loop: ModelAnimationLoop.REPEAT,
        });

        flag = requestAnimationFrame(forward);
      }
    };

    const forward = () => {
      speedVector = Cartesian3.multiplyByScalar(
        Cartesian3.UNIT_X,
        speed / 2,
        speedVector
      );
      position = Matrix4.multiplyByPoint(
        planePrimitive.modelMatrix,
        speedVector,
        position
      );
      pathPosition.addSample(JulianDate.now(), position);
      Transforms.headingPitchRollToFixedFrame(
        position,
        hpRoll,
        Ellipsoid.WGS84,
        fixedFrameTransform,
        planePrimitive.modelMatrix
      );

      const { center } = planePrimitive.boundingSphere;
      hpRange.heading = hpRoll.heading; // 镜头前后
      hpRange.pitch = hpRoll.pitch - 0.05 * Math.PI; // 镜头上下
      hpRange.range = r * 10; // 镜头远近
      camera.lookAt(center, hpRange);

      if (flag) {
        flag = requestAnimationFrame(forward);
      }
    };
  } catch (error) {
    console.log(`Error loading model: ${error}`);
  }
};
</script>

<template>
  <div id="cesiumContainer" class="czm-ctnr"></div>
</template>

<style lang="scss" scoped>
.czm-ctnr {
  width: 100%;
  height: 100%;
}
</style>
